<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第17章 - symfonyを拡張する</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="16-Application-Management-Tools.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="18-Performance.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.17.extending.symfony" id="chapter.17.extending.symfony"></a><h1>第17章 - symfonyを拡張する</h1>

<p>結局のところ、symfonyのふるまいを変える必要があります。特定のクラスが振る舞う方法を修正するもしくは独自のカスタム機能を追加する必要があるにせよ、変更作業は必然です。symfonyが予想できない固有の要望を顧客が持つからです。実際のところ、この状況はよくありふれたことなのでsymfonyは既存のクラスを拡張するメカニズムを提供します。このメカニズムはミックスイン(mixin)と呼ばれます。ファクトリ(factory)の設定を利用してsymfonyのコアクラスをあなた独自のクラスに置き換えることもできます。いったん拡張機能(エクステンション)を作れば、プラグインとして簡単にパッケージにできるので、ほかのアプリケーションもしくはほかのsymfonyのユーザーが再利用できます。</p>

<div class="toc">
<dl>
<dt><a href="#mixins">17.1. ミックスイン</a></dt>
<dd><dl>
<dt><a href="#understanding.multiple.inheritance">17.1.1. 多重継承を理解する</a></dt>
<dt><a href="#mixing.classes">17.1.2. クラスをミックスする</a></dt>
<dt><a href="#declaring.a.class.as.extendable">17.1.3. クラスを拡張可能なものとして宣言する</a></dt>
<dt><a href="#registering.extensions">17.1.4. 拡張機能を登録する</a></dt>
<dt><a href="#extending.with.more.precision">17.1.5. より精密に拡張する</a></dt>
</dl></dd>
<dt><a href="#factories">17.2. ファクトリ</a></dt>
<dt><a href="#bridges.to.other.frameworks.components">17.3. ほかのフレームワークへのブリッジ</a></dt>
<dt><a href="#plugins">17.4. プラグイン</a></dt>
<dd><dl>
<dt><a href="#finding.symfony.plugins">17.4.1. symfonyのプラグインを見つける</a></dt>
<dt><a href="#installing.a.plugin">17.4.2. プラグインをインストールする</a></dt>
<dt><a href="#pear.plugins">17.4.3. PEARプラグイン</a></dt>
<dd><dl>
<dt><a href="#archive.plugins">17.4.3.1. アーカイブのプラグイン</a></dt>
<dt><a href="#installing.plugins.from.a.version.control.repository">17.4.3.2. バージョン管理システムのリポジトリからプラグインをインストールする</a></dt>
<dt><a href="#activating.a.plugin.module">17.4.3.3. プラグインモジュールを有効にする</a></dt>
<dt><a href="#listing.the.installed.plugins">17.4.3.4. インストールしたプラグインの一覧を表示する</a></dt>
<dt><a href="#upgrading.and.uninstalling.plugins">17.4.3.5. プラグインのアップグレードとアンインストール</a></dt>
</dl></dd>
<dt><a href="#anatomy.of.a.plugin">17.4.4. プラグインの分析</a></dt>
<dd><dl>
<dt><a href="#plugin.file.structure">17.4.4.1. プラグインのファイル構造</a></dt>
<dt><a href="#plugin.abilities">17.4.4.2. プラグインの機能</a></dt>
<dt><a href="#manual.plugin.setup">17.4.4.3. 手動によるプラグインのセットアップ</a></dt>
<dt><a href="#customizing.a.plugin.for.an.application">17.4.4.4. アプリケーションのためにプラグインをカスタマイズする</a></dt>
</dl></dd>
<dt><a href="#how.to.write.a.plugin">17.4.5. プラグインの書き方</a></dt>
<dd><dl>
<dt><a href="#file.organization">17.4.5.1. ファイルのコンフィギュレーション</a></dt>
<dt><a href="#creating.the.package.xml.file">17.4.5.2. package.xmlファイルを作る</a></dt>
<dt><a href="#contents">17.4.5.3. 内容</a></dt>
<dt><a href="#plugin.dependencies">17.4.5.4. プラグインの依存関係</a></dt>
<dt><a href="#building.the.plugin">17.4.5.5. プラグインをビルドする</a></dt>
<dt><a href="#hosting.your.plugin.in.the.symfony.project.website">17.4.5.6. 公式サイトでプラグインを配布する</a></dt>
<dt><a href="#naming.conventions">17.4.5.7. 命名規約</a></dt>
</dl></dd></dl></dd>
<dt><a href="#summary">17.5. まとめ</a></dt>
</dl>
</div>
<a name="mixins" id="mixins"></a><h2>ミックスイン</h2>

<p>PHPの現在の制約のなかで、もっとも悩ましいことは複数のクラスを継承できないことです。別の制約は既存のクラスに新しいメソッドを追加するもしくは既存のメソッドをオーバーライドできないことです。これら2つの制約を緩和し、symfonyを本当に拡張できるようにするため、symfonyは<code>sfMixer</code>と呼ばれるクラスを導入します。調理器具に関連する機能ではありませんが、ミックスインの概念はオブジェクト指向のプログラミングで見つかります(訳注：Rubyなど)。ミックスインとは拡張するためにクラスにミックスされるメソッドもしくは関数のグループです。</p>

<a name="understanding.multiple.inheritance" id="understanding.multiple.inheritance"></a><h3>多重継承を理解する</h3>

<p>多重継承は複数のクラスを拡張するためのクラスの機能で、これらのクラスプロパティとメソッドを継承します。例を考えてみましょう。リスト17-1のように、それぞれが独自のプロパティとメソッドを持つ<code>Story</code>クラスと<code>Book</code>クラスを想像してください。</p>

<p>リスト17-1 - 2つの例のクラス</p>

<pre class="php"><span class="kw2">class</span> Story
<span class="br0">&#123;</span>
  protected <span class="re0">$title</span> <span class="sy0">=</span> <span class="st_h">''</span><span class="sy0">;</span>
  protected <span class="re0">$topic</span> <span class="sy0">=</span> <span class="st_h">''</span><span class="sy0">;</span>
  protected <span class="re0">$characters</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> __construct<span class="br0">&#40;</span><span class="re0">$title</span> <span class="sy0">=</span> <span class="st_h">''</span><span class="sy0">,</span> <span class="re0">$topic</span> <span class="sy0">=</span> <span class="st_h">''</span><span class="sy0">,</span> <span class="re0">$characters</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">title</span> <span class="sy0">=</span> <span class="re0">$title</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">topic</span> <span class="sy0">=</span> <span class="re0">$topic</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">characters</span> <span class="sy0">=</span> <span class="re0">$characters</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> getSummary<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">title</span><span class="sy0">.</span><span class="st_h">', a story about '</span><span class="sy0">.</span><span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">topic</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> Book
<span class="br0">&#123;</span>
  protected <span class="re0">$isbn</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">function</span> setISBN<span class="br0">&#40;</span><span class="re0">$isbn</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">isbn</span> <span class="sy0">=</span> <span class="re0">$isbn</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> getISBN<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">isbn</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p><code>ShortStory</code>クラスは<code>Story</code>クラスを継承し、<code>ComputerBook</code>クラスは<code>Book</code>クラスを継承し、論理的に、<code>Novel</code>クラスは<code>Story</code>クラスと<code>Book</code>クラスを継承して、これらすべてのメソッドを利用します。不幸なことに、PHPでは多重継承は不可能です。リスト17-2のように<code>Novel</code>宣言を書くことはできません。</p>

<p>リスト17-2 - PHPでは多重継承は不可能である</p>

<pre class="php"><span class="kw2">class</span> Novel <span class="kw2">extends</span> Story<span class="sy0">,</span> Book
<span class="br0">&#123;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="re0">$myNovel</span> <span class="sy0">=</span> <span class="kw2">new</span> Novel<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$myNovel</span><span class="sy0">-&gt;</span><span class="me1">getISBN</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p><code>Novel</code>クラスが2つのクラスを拡張する代わりに2つのインターフェイスを実装できる可能性がありますが、この方法では親クラスで実際にクラスを書くことができません。</p>

<a name="mixing.classes" id="mixing.classes"></a><h3>クラスをミックスする</h3>

<p><code>sfMixer</code>クラスは問題に対して別のアプローチをとります。クラスが適切なフックを含むという前提のもとで、既存のクラスを受けとり、後天的にこれを拡張します。この手順では2つのステップを含みます。</p>

<ul>
<li>クラスを拡張可能なものとして宣言する</li>
<li>クラス宣言の後に拡張機能(もしくはミックスイン)を登録する</li>
</ul>

<p>リスト17-3は<code>sfMixer</code>クラスで<code>Novel</code>クラスを実装する方法を示しています。</p>

<p>リスト17-3 - <code>sfMixer</code>を通して多重継承は可能である</p>

<pre class="php"><span class="kw2">class</span> Novel <span class="kw2">extends</span> Story
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> __call<span class="br0">&#40;</span><span class="re0">$method</span><span class="sy0">,</span> <span class="re0">$arguments</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'Novel'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'Book'</span><span class="sy0">,</span> <span class="st_h">'getISBN'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$myNovel</span> <span class="sy0">=</span> <span class="kw2">new</span> Novel<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$myNovel</span><span class="sy0">-&gt;</span><span class="me1">getISBN</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>クラスの1つ(<code>Story</code>)はメインの親として選択され、1つのクラスからのみ継承できるというPHPの機能と一致しています。<code>Novel</code>クラスは<code>__call()</code>メソッドに設置されたコードによって拡張可能なものとして宣言されます。ほかのクラスのメソッド(<code>Book</code>)は<code>sfMixer::register()</code>への呼び出しによってあとで<code>Novel</code>クラスに追加されます。つぎのセクションではこのプロセスをわかりやすく説明します。</p>

<p><code>Novel</code>クラスの<code>getISBN()</code>メソッドが呼び出されたとき、あたかもリスト17-2で定義されたクラスのようにすべてのことが起こります。それをシミュレートするのが<code>__call()</code>マジックメソッドと<code>sfMixer</code>staticメソッドであること以外は。<code>getISBN()</code>メソッドは<code>Novel</code>クラスにミックスされます。</p>

<blockquote class="sidebar"><p class="title">
  ミックスインを使うとき</p>
  
  <p>多くの場合でsymfonyのミックスインのメカニズムは便利です。以前説明したような多重継承をシミュレートすることもこれらのメカニズムの1つです。</p>
  
  <p>宣言したあとでメソッドを変更するためにミックスインを利用できます。たとえば、グラフィックライブラリを作成するとき、おそらくは線を表す<code>Line</code>オブジェクトを実装することになります。このオブジェクトは4つの属性(端の座標)とそれ自身をレンダリングする<code>draw()</code>メソッドを持ちます。<code>ColoredLine</code>オブジェクトは同じプロパティとメソッドを持ちますが、色を指定する追加の<code>color</code>属性を持ちます。さらに、<code>ColoredLine</code>オブジェクトの<code>draw()</code>メソッドは、オブジェクトの色に関して、シンプルな<code>Line</code>のメソッドとは少し異なります。色を処理するグラフィカルな要素の機能を<code>ColoredElement</code>クラスにパッケージにすることができました。これによってほかのグラフィカルな要素のために<code>color</code>メソッドを再利用できるようになります(<code>Dot</code>、<code>Polygon</code>など)。この場合、<code>ColoredLine</code>オブジェクトの理想的な実装は、ミックスインした<code>ColoredElement</code>クラスからのメソッドで<code>Line</code>クラスの拡張です。最後の<code>draw()</code>メソッドは<code>Line</code>からのオリジナルのメソッドと<code>ColoredElement</code>からのメソッドとのミックスになります。</p>
  
  <p>ミックスインは新しいメソッドを既存のクラスに追加する方法とみなすこともできます。たとえば、<code>sfActions</code>と呼ばれるsymfonyのアクションクラスはsymfony内部で定義されます。PHPの制約の一つは初期に宣言したあとで<code>sfActions</code>の定義を変更できないことです。アプリケーションの1つだけに<code>sfActions</code>にカスタムメソッドを追加したい場合があるかもしれません。たとえば、リクエストを特別なWebサービスに転送することです。この目的のために、PHPだけでは不十分ですが、ミックスインのメカニズムは完璧な解決方法を提供します。</p>
</blockquote>

<a name="declaring.a.class.as.extendable" id="declaring.a.class.as.extendable"></a><h3>クラスを拡張可能なものとして宣言する</h3>

<p>クラスを拡張できるように宣言するには、1つもしくは複数の"フック"(hook)をコードに挿入しなければなりません。<code>sfMixer</code>クラスはフックによってあとでコードを識別できます。これらのフックは<code>sfMixer::callMixins()</code>メソッドへの呼び出しです。多くのsymfonyのクラス、<code>sfRequest</code>、<code>sfResponse</code>、<code>sfController</code>、<code>sfUser</code>、<code>sfAction</code>などにフックがすでに含まれています。</p>

<p>フックは望む拡張性の程度に応じて、クラスの異なる部分に設置できます:</p>

<ul>
<li>新しいメソッドをクラスに追加できるようにするため、リスト17-4で示されているように、フックを<code>__call()</code>メソッドに挿入して、結果を返さなければなりません。</li>
</ul>

<p>リスト17-4 - クラスに新しいメソッドを取得する機能を与える</p>

<pre class="php"><span class="kw2">class</span> SomeClass
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> __call<span class="br0">&#40;</span><span class="re0">$method</span><span class="sy0">,</span> <span class="re0">$arguments</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<ul>
<li>既存のメソッドの動作方法を変えるために、リスト17-5で示されているように、フックをメソッドの内側に挿入しなければなりません。ミックスインのクラスによって追加されたコードはフックが設置された場所で実行されます。</li>
</ul>

<p>リスト17-5 - メソッドに変更できる機能を与える</p>

<pre class="php"><span class="kw2">class</span> SomeOtherClass
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> doThings<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">echo</span> <span class="st0">&quot;I'm working...&quot;</span><span class="sy0">;</span>
    sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>メソッドに複数のフックを追加できます。この場合、フックを命名しなければなりませんので、リスト17-6で示されているように、どのフックがあとで実行されるのかを定義することができます。名前つきのフックはフックの名前をパラメーターとして<code>callMixins()</code>を呼び出します。実行されるミックスインのコードの場所を伝えるために、ミックスインを登録するときに、この名前があとで使われます。</p>

<p>リスト17-6 - メソッドは複数のフックを含むことができる。この場合フックに名前をつけなければならない</p>

<pre class="php"><span class="kw2">class</span> AgainAnotherClass
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> doMoreThings<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">echo</span> <span class="st0">&quot;I'm ready.&quot;</span><span class="sy0">;</span>
    sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="st_h">'beginning'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">echo</span> <span class="st0">&quot;I'm working...&quot;</span><span class="sy0">;</span>
    sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="st_h">'end'</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="kw1">echo</span> <span class="st0">&quot;I'm done.&quot;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>もちろん、リスト17-7が示すように、クラスを作るテクニックを新しく拡張可能なメソッドに割り当てる機能に結びつけることができます。</p>

<p>リスト17-7 - クラスはさまざまな方法で拡張可能である</p>

<pre class="php"><span class="kw2">class</span> BicycleRider
<span class="br0">&#123;</span>
  protected <span class="re0">$name</span> <span class="sy0">=</span> <span class="st_h">'John'</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> getName<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">name</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> sprint<span class="br0">&#40;</span><span class="re0">$distance</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">echo</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">name</span><span class="sy0">.</span><span class="st0">&quot;は&quot;</span><span class="sy0">.</span><span class="re0">$distance</span><span class="sy0">.</span><span class="st0">&quot;メートルをダッシュする<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
    sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// sprint()メソッドは拡張可能</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> climb<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">echo</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">name</span><span class="sy0">.</span><span class="st_h">'はよじ登る'</span><span class="sy0">;</span>
    sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="st_h">'slope'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// climb()はここで拡張可能</span>
    <span class="kw1">echo</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">name</span><span class="sy0">.</span><span class="st_h">'は山頂に到達する'</span><span class="sy0">;</span>
    sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="st_h">'top'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// そしてここでも</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> __call<span class="br0">&#40;</span><span class="re0">$method</span><span class="sy0">,</span> <span class="re0">$arguments</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> sfMixer<span class="sy0">::</span><span class="me2">callMixins</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// BicyleRiderクラスは拡張可能</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<blockquote class="caution"><p>
  拡張可能なものとして宣言されたクラスだけが<code>sfMixer</code>によって拡張可能です。このことはこのサービスを"購読"(subscribe)しなかったクラスを拡張するためにこのメカニズムを利用できないことを意味します。</p>
</blockquote>

<a name="registering.extensions" id="registering.extensions"></a><h3>拡張機能を登録する</h3>

<p>拡張機能を既存のフックに登録するには、<code>sfMixer::register()</code>メソッドを使います。最初の引数は拡張する要素で、2番目の引数はPHPで呼び出し可能なもので、ミックスインを表します。</p>

<p>最初の引数の形式は拡張したいものに依ります:</p>

<ul>
<li>クラスを拡張する場合、クラスの名前を使います。</li>
<li>匿名のフックでメソッドを拡張する場合、<code>class::method</code>パターンを使います。</li>
<li>名前つきのフックでメソッドを拡張する場合、<code>class::method:hook</code>パターンを使います。</li>
</ul>

<p>リスト17-8はリスト17-7で定義されたクラスを拡張することでこの原則を説明しています。拡張されたオブジェクトは自動的に最初の引数として<code>Mixin</code>メソッドに渡されます(もちろん、拡張されたメソッドがstaticであることは除きます)。ミックスインメソッドはオリジナルのメソッド呼び出しのパラメーターも利用します。</p>

<p>リスト17-8 - 拡張機能を登録する</p>

<pre class="php"><span class="kw2">class</span> Steroids
<span class="br0">&#123;</span>
  protected <span class="re0">$brand</span> <span class="sy0">=</span> <span class="st_h">'foobar'</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> partyAllNight<span class="br0">&#40;</span><span class="re0">$bicycleRider</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">echo</span> <span class="re0">$bicycleRider</span><span class="sy0">-&gt;</span><span class="me1">getName</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st0">&quot;は夜にダンスをして時間を費やす。<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
    <span class="kw1">echo</span> <span class="st0">&quot;ありがとう&quot;</span><span class="sy0">.</span><span class="re0">$brand</span><span class="sy0">.</span><span class="st0">&quot;！<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> breakRecord<span class="br0">&#40;</span><span class="re0">$bicycleRider</span><span class="sy0">,</span> <span class="re0">$distance</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">echo</span> <span class="st0">&quot;誰も&quot;</span><span class="sy0">.</span><span class="re0">$distance</span><span class="sy0">.</span><span class="st0">&quot;メートルより速い記録を出したことはない！<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  static <span class="kw2">function</span> pass<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">echo</span> <span class="st0">&quot;。そして先頭集団の半分を超す。<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'Steroids'</span><span class="sy0">,</span> <span class="st_h">'partyAllNight'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider:sprint'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'Steroids'</span><span class="sy0">,</span> <span class="st_h">'breakRecord'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider:climb:slope'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'Steroids'</span><span class="sy0">,</span> <span class="st_h">'pass'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider:climb:top'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'Steroids'</span><span class="sy0">,</span> <span class="st_h">'pass'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="re0">$superRider</span> <span class="sy0">=</span> <span class="kw2">new</span> BicycleRider<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$superRider</span><span class="sy0">-&gt;</span><span class="me1">climb</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">=&gt;</span> Johnはよじ登る。そして先頭集団の半分を超す。
<span class="sy0">=&gt;</span> Johnは山頂に到達する。そして先頭集団の半分を超す。
<span class="re0">$superRider</span><span class="sy0">-&gt;</span><span class="me1">sprint</span><span class="br0">&#40;</span><span class="nu0">2000</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">=&gt;</span> Johnは<span class="nu0">2000</span>メートルをダッシュする
<span class="sy0">=&gt;</span> 誰も<span class="nu0">2000</span>メートルよりも速い記録を出したことがない！
<span class="re0">$superRider</span><span class="sy0">-&gt;</span><span class="me1">partyAllNight</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">=&gt;</span> Johnは夜にダンスをして時間を費やす。
<span class="sy0">=&gt;</span> ありがとうfoobar！</pre>

<p>拡張メカニズムはメソッドを追加するだけではありません。<code>partyAllNight()</code>メソッドは<code>Steroids</code>クラスの属性を利用します。<code>Steroids</code>クラスのメソッドで<code>BicycleRider</code>クラスを拡張するとき、<code>BicycleRider</code>オブジェクトの内側に新しい<code>Steroids</code>インスタンスを実際に作ります。</p>

<blockquote class="caution"><p>
  同じ名前を持つ2つのメソッドを既存のクラスに追加できません。<code>__call()</code>メソッド内の<code>callMixins()</code>呼び出しがミックスインのメソッド名をキーとして使うからです、同じように、クラスのメソッドと同じ名前のメソッドをそのクラスに追加することもできません。ミックスインのメカニズムが<code>__call()</code>マジックメソッドに依存しており、この特殊なケースにおいては、これは決して呼び出しされないからです。</p>
</blockquote>

<p><code>register()</code>呼び出しの2番目の引数はPHPで呼び出し可能なので、<code>class::method</code>配列か<code>object-&gt;method</code>配列か、関数名になることができます。リスト17-9の例をご覧ください。</p>

<p>リスト17-9 - PHPで呼び出し可能なものはmixer拡張機能として登録できる</p>

<pre class="php"><span class="co1">// PHPで呼び出し可能なものとしてクラスメソッドを使う</span>
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'Steroids'</span><span class="sy0">,</span> <span class="st_h">'partyAllNight'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// PHPで呼び出し可能なものとしてオブジェクトメソッドを使う</span>
<span class="re0">$mySteroids</span> <span class="sy0">=</span> <span class="kw2">new</span> Steroids<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="re0">$mySteroids</span><span class="sy0">,</span> <span class="st_h">'partyAllNight'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// PHPで呼び出し可能なものとして関数を使う</span>
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider'</span><span class="sy0">,</span> <span class="st_h">'die'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>拡張機能のメカニズムは動的であり、すでにオブジェクトをインスタンス化していても、クラスでさらなる拡張機能を利用できることを意味します。リスト17-10の例をご覧ください。</p>

<p>リスト17-10 - 拡張機能のメカニズムは動的でインスタンス化のあとでも行われれる</p>

<pre class="php"><span class="re0">$simpleRider</span> <span class="sy0">=</span> <span class="kw2">new</span> BicycleRider<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$simpleRider</span><span class="sy0">-&gt;</span><span class="me1">sprint</span><span class="br0">&#40;</span><span class="nu0">500</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">=&gt;</span> Johnは<span class="nu0">500</span>メートルをダッシュする
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'BicycleRider:sprint'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'Steroids'</span><span class="sy0">,</span> <span class="st_h">'breakRecord'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$simpleRider</span><span class="sy0">-&gt;</span><span class="me1">sprint</span><span class="br0">&#40;</span><span class="nu0">500</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">=&gt;</span> Johnは<span class="nu0">500</span>メートルをダッシュする
<span class="sy0">=&gt;</span> 誰も<span class="nu0">500</span>メートルより速い記録を出したことがない！</pre>

<a name="extending.with.more.precision" id="extending.with.more.precision"></a><h3>より精密に拡張する</h3>

<p><code>sfMixer::callMixins()</code>のインストラクションは実際には少し手の込んだものへのショートカットです。これは現在のオブジェクトと現在のメソッドのパラメーターを渡しながら、自動的に登録されたミックスインのリストをループしてこれらを1つずつ呼び出しています。要するに、<code>sfMixer::callMixins()</code>呼び出しはおよそリスト17-11のようにふるまいます。</p>

<p>リスト17-11 - <code>callMixin()</code>は登録されたミックスインをループして、これらを実行する</p>

<pre class="php"><span class="kw1">foreach</span> <span class="br0">&#40;</span>sfMixer<span class="sy0">::</span><span class="me2">getCallables</span><span class="br0">&#40;</span><span class="re0">$class</span><span class="sy0">.</span><span class="st_h">':'</span><span class="sy0">.</span><span class="re0">$method</span><span class="sy0">.</span><span class="st_h">':'</span><span class="sy0">.</span><span class="re0">$hookName</span><span class="br0">&#41;</span> <span class="kw1">as</span> <span class="re0">$callable</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw3">call_user_func_array</span><span class="br0">&#40;</span><span class="re0">$callable</span><span class="sy0">,</span> <span class="re0">$parameters</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>ほかのパラメーターを渡したい場合もしくは戻り値で特別な何かをしたい場合、ショートカットのメソッドを利用する代わりに<code>foreach</code>ループを明示的に書けます。ミックスインがクラスに統合される例についてリスト17-12をご覧ください。</p>

<p>リスト17-12 - カスタムループで<code>callMixin()</code>を置き換える</p>

<pre class="php"><span class="kw2">class</span> Income
<span class="br0">&#123;</span>
  protected <span class="re0">$amount</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> calculateTaxes<span class="br0">&#40;</span><span class="re0">$rate</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$taxes</span> <span class="sy0">=</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">amount</span> <span class="sy0">*</span> <span class="re0">$rate</span><span class="sy0">;</span>
    <span class="kw1">foreach</span> <span class="br0">&#40;</span>sfMixer<span class="sy0">::</span><span class="me2">getCallables</span><span class="br0">&#40;</span><span class="st_h">'Income:calculateTaxes'</span><span class="br0">&#41;</span> <span class="kw1">as</span> <span class="re0">$callable</span><span class="br0">&#41;</span>
    <span class="br0">&#123;</span>
      <span class="re0">$taxes</span> <span class="sy0">+=</span> <span class="kw3">call_user_func</span><span class="br0">&#40;</span><span class="re0">$callable</span><span class="sy0">,</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">amount</span><span class="sy0">,</span> <span class="re0">$rate</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="br0">&#125;</span>
&nbsp;
    <span class="kw1">return</span> <span class="re0">$taxes</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">class</span> FixedTax
<span class="br0">&#123;</span>
  protected <span class="re0">$minIncome</span> <span class="sy0">=</span> <span class="nu0">10000</span><span class="sy0">;</span>
  protected <span class="re0">$taxAmount</span> <span class="sy0">=</span> <span class="nu0">500</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> calculateTaxes<span class="br0">&#40;</span><span class="re0">$amount</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <span class="br0">&#40;</span><span class="re0">$amount</span> <span class="sy0">&gt;</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">minIncome</span><span class="br0">&#41;</span> ? <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">taxAmount</span> <span class="sy0">:</span> <span class="nu0">0</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
sfMixer<span class="sy0">::</span><span class="me2">register</span><span class="br0">&#40;</span><span class="st_h">'Income:calculateTaxes'</span><span class="sy0">,</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="st_h">'FixedTax'</span><span class="sy0">,</span> <span class="st_h">'calculateTaxes'</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<blockquote class="sidebar"><p class="title">
  Propelのビヘイビアー</p>
  
  <p>以前8章で説明しましたが、Propelのビヘイビアー(behavior)は特殊なミックスインです: これらはPropelが生成したオブジェクトを拡張します。例を見てみましょう。</p>
  
  <p>Propelのオブジェクトはすべてが<code>delete()</code>メソッドを持つデータベースのテーブルに対応します。<code>delete</code>メソッドはデータベースから関連するレコードを削除します。しかし、<code>Invoice</code>クラスに対して、レコードを削除できないようにするために、データベース内部のレコードを維持できるように<code>delete()</code>メソッドを変更して、<code>is_deleted</code>属性の値をtrueに変更したい場合があります。通常のオブジェクトの読みとりメソッド(<code>doSelect()</code>、<code>retrieveByPk()</code>)は<code>is_deleted</code>が<code>false</code>であるとレコードとみなすだけです。<code>forceDelete()</code>と呼ばれる別のメソッドを追加する必要があります。このメソッドによってレコードを本当に削除できます。実際、これらすべての修正は<code>ParanoidBehavior</code>と呼ばれる新しいクラスにまとめられます。最後の<code>Invoice</code>クラスはPropelの<code>BaseInvoice</code>クラスを拡張し、ミックスインされた<code>ParanoidBehaviorMixin</code>のメソッドを持ちます。</p>
  
  <p>ビヘイビアーはPropelオブジェクト上のミックスインです。実際に、symfonyにおいて"ビヘイビアー"という用語は複数の内容をカバーします: ミックスインはプラグインとしてパッケージになります。丁度述べた<code>ParanoidBehavior</code>クラスは<code>sfPropelParanoidBehaivorPlugin</code>と呼ばれる実在するsymfonyのプラグインに対応します。インストールとこのプラグインの使いかたに関する詳細な内容はsymfonyの公式サイトのwiki(<a href="http://trac.symfony-project.org/wiki/sfPropelParanoidBehaviorPlugin">http://trac.symfony-project.org/wiki/sfPropelParanoidBehaviorPlugin</a>)を参照してください。</p>
  
  <p>ビヘイビアーに関して最後の一言です: これらをサポートできるようにするには、生成されたPropelのオブジェクトは多くのフックを含まなければなりません。ビヘイビアーを使わない場合、これらが実行を遅くしてパフォーマンスにペナルティを課す可能性があります。フックがデフォルトで有効になっていないのはそういうわけです。ビヘイビアーのサポートを有効にするには、最初に<code>propel.ini</code>ファイルのなかで<code>propel.builder.addBehaviors</code>プロパティを<code>true</code>に設定してモデルをリビルドしなければなりません。</p>
</blockquote>

<a name="factories" id="factories"></a><h2>ファクトリ</h2>

<p>ファクトリ(factory)は特定のタスクのためのクラスの定義です。symfonyはコントローラーやセッション機能などのコア機能についてファクトリに依存します。たとえば、symfonyが新しいリクエストオブジェクトを作る必要がある場合、symfonyはこの目的のために使うクラスの名前のためにファクトリの定義を検索します。リクエストのためのデフォルトのファクトリの定義は<code>sfWebRequest</code>クラスで、symfonyはリクエストを処理するためにこのクラスのオブジェクトを作ります。ファクトリの定義を使う利点はsymfonyのコア機能をとても簡単に変更できることです: ファクトリの定義を変更し、symfonyは自身の代わりにカスタムリクエストクラスを使います。</p>

<p>ファクトリの定義は<code>factories.yml</code>設定ファイルに保存されます。リスト17-3はデフォルトのファクトリの定義ファイルを示します。それぞれの定義はオートロードされるクラスの名前と(オプションとしての)パラメーターの一式から構成されます。たとえば、セッションストレージのファクトリ(<code>storage:</code>キーの下に設定する)は一貫したセッションを可能にするためにクライアントコンピュータ上で作成されたCookieに名前をつける<code>session_name</code>パラメーターを使います。</p>

<p>リスト17-13 - デフォルトのファクトリファイル(<code>myapp/config/factories.yml</code>)</p>

<pre><code>cli:
  controller:
    class: sfConsoleController
  request:
    class: sfConsoleRequest

test:
  storage:
    class: sfSessionTestStorage

#all:
#  controller:
#    class: sfFrontWebController
#
#  request:
#    class: sfWebRequest
#
#  response:
#    class: sfWebResponse
#
#  user:
#    class: myUser
#
#  storage:
#    class: sfSessionStorage
#    param:
#      session_name: symfony
#
#  view_cache:
#    class: sfFileCache
#    param:
#      automaticCleaningFactor: 0
#      cacheDir:                %SF_TEMPLATE_CACHE_DIR%
</code></pre>

<p>ファクトリを変更する最良の方法はデフォルトのファクトリから継承した新しいクラスを作り、新しいメソッドをそのクラスに追加することです。たとえば、ユーザーセッションのファクトリは<code>myUser</code>クラス(<code>myapp/lib/</code>ディレクトリに設置)に設定され、<code>sfUser</code>クラスから継承します。既存のファクトリを利用するには同じメカニズムを使います。リスト17-14はリクエストオブジェクトのための新しいファクトリの例を示しています。</p>

<p>リスト17-14 - ファクトリをオーバーライドする</p>

<pre class="php">//オートロードされたディレクトリ内でmyRequest.class.phpを作る
// たとえばmyapp/lib/において
<span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw2">class</span> myRequest <span class="kw2">extends</span> sfRequest
<span class="br0">&#123;</span>
  <span class="co1">// あなたのコードをここに</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// factories.ymlでクラスをリクエストファクトリとして宣言する</span>
all<span class="sy0">:</span>
  request<span class="sy0">:</span>
    <span class="kw2">class</span><span class="sy0">:</span> myRequest</pre>

<a name="bridges.to.other.frameworks.components" id="bridges.to.other.frameworks.components"></a><h2>ほかのフレームワークへのブリッジ</h2>

<p>サードパーティのクラスによって提供された機能が必要な場合、このクラスを<code>lib/</code>ディレクトリの1つにコピーしたくない場合、おそらくはsymfonyがファイルを探す通常の場所の外側にそのクラスをインストールすることになります。この場合、クラスを利用するには、オートロードを利用するsymfonyのブリッジ機能を使わないかぎり、<code>requre</code>ステートメントを手動でコードに含めることになります。</p>

<p>symfonyは(まだ)すべてのためのツールを提供していません。PDFジェネレーター、Google MapsのAPI、PHPによるLucene検索エンジンの実装など、おそらくZend Frameworkからいくつかのライブラリが必要になります。PHPで直接イメージを操作する、Eメールを読むためにPOP3アカウントに接続する、コンソールのインターフェイスを設計することなどを行いたい場合、eZcomponentsからライブラリを選ぶことがあるかもしれません。幸いにして、正しい設定を定義をすると、これらのライブラリからのコンポーネントはsymfonyで正常に動作します。</p>

<p>(PEAR経由でサードパーティのライブラリをインストールしないかぎり)最初に宣言するものはライブラリのrootディレクトリへのパスです。これはアプリケーションの<code>settings.yml</code>設定ファイルで行われます:</p>

<pre><code>.settings:
  zend_lib_dir:   /usr/local/zend/library/
  ez_lib_dir:     /usr/local/ezcomponents/
</code></pre>

<p>それから、symfonyでオートロードが失敗するとき考慮するライブラリを指定することでオートロードのルーチンを拡張します:</p>

<pre><code>.settings:
  autoloading_functions:
    - [sfZendFrameworkBridge, autoload]
    - [sfEzComponentsBridge,  autoload]
</code></pre>

<p>この設定は<code>autoload.yml</code>で定義されたルールとは異なります(このルールに関する詳しい情報は19章を参照)。<code>autoloading_functions</code>設定はブリッジクラスを指定し、<code>autoload.yml</code>ファイルは検索のためのパスとルールを指定します。つぎの内容はロードされていないクラスの新しいオブジェクトを作るときに起きることを説明しています:</p>

<ol>
<li>symfonyのオートロード機能(<code>sfCore::spAutoload()</code>)は最初<code>autoload.yml</code>ファイルで宣言されたパスでクラスを探します。</li>
<li>何も見つからない場合、<code>sf_autoloading_functions</code>設定で宣言されたコールバックメソッドが順番に呼び出されます。これらの1つが<code>true</code>を返すまで続きます</li>
<li><code>sfZendFrameworkBridge::autoload()</code></li>
<li><code>sfEzComponentsBridge::autoload()</code></li>
<li>これらが<code>false</code>を返す場合、PHP 5.0.Xを利用しているのであればsymfonyはクラスが存在しないことを伝える例外を投じ、PHP 5.1を利用している場合、エラーはPHP自身によって作られます。</li>
</ol>

<p>このことはほかのフレームワークコンポーネントがオートロードメカニズムから恩恵を受けることを意味し、独自の環境よりもこれらを簡単に利用できます。たとえば、PHPのLucene検索エンジンと同等のものを実装するためにZend Frameworkの<code>Zend_Search</code>コンポーネントを使いたい場合、つぎのように書かなければなりません:</p>

<pre class="php"><span class="kw1">require_once</span> <span class="st_h">'Zend/Search/Lucene.php'</span><span class="sy0">;</span>
<span class="re0">$doc</span> <span class="sy0">=</span> <span class="kw2">new</span> Zend_Search_Lucene_Document<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$doc</span><span class="sy0">-&gt;</span><span class="me1">addField</span><span class="br0">&#40;</span>Zend_Search_Lucene_Field<span class="sy0">::</span><span class="me2">Text</span><span class="br0">&#40;</span><span class="st_h">'url'</span><span class="sy0">,</span> <span class="re0">$docUrl</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">...</span></pre>

<p>symfonyとZend Frameworkのブリッジ機能によって、上記のコードはよりシンプルになります。つぎのように書くだけです:</p>

<pre class="php"><span class="re0">$doc</span> <span class="sy0">=</span> <span class="kw2">new</span> Zend_Search_Lucene_Document<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="co1">// クラスがオートロードされた</span>
<span class="re0">$doc</span><span class="sy0">-&gt;</span><span class="me1">addField</span><span class="br0">&#40;</span>Zend_Search_Lucene_Field<span class="sy0">::</span><span class="me2">Text</span><span class="br0">&#40;</span><span class="st_h">'url'</span><span class="sy0">,</span> <span class="re0">$docUrl</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="sy0">...</span></pre>

<p>利用可能なブリッジ機能は<code>$sf_symfony_lib_dir/addon/bridge/</code>ディレクトリに保存されます。</p>

<a name="plugins" id="plugins"></a><h2>プラグイン</h2>

<p>1つのsymfonyアプリケーションの1つのために開発したコードピースの再利用がおそらく必要になります。このコードのピースを単独のクラスのパッケージにすることができるのであれば、問題ありません: クラスを別のアプリケーションの<code>lib/</code>フォルダーの1つに設置すればオートローダが残りを引き受けます。しかし、コードが複数のファイルに散在している場合、たとえば、administrationジェネレーター用の完全に新しいテーマ、もしくは好みの視覚効果を自動化するJavaScriptファイルとヘルパーの組み合わせなどの場合、ファイルをコピーするだけの方法は最良の解決方法ではありません。</p>

<p>プラグインはいくつかのファイルにまたがるコードをパッケージにする方法と、いくつかのプロジェクトをまたがってこのコードを再利用する方法を提供します。プラグインのなかで、クラス、フィルター、ミックスイン、ヘルパー、設定、タスク、モジュール、スキーマ、モデルの拡張、フィクスチャ、Webアセットなどをパッケージにすることができます。プラグインをインストール、アップグレード、アンインストールする方法は簡単です。これらは<code>.tgz</code>アーカイブ、PEARパッケージ、もしくはコードリポジトリとして配布可能で、コードのリポジトリから簡単にチェックアウトできます。PEARのパッケージとなったプラグインは依存関係の管理機能を利用し、アップグレードと自動検出が簡単です。symfonyのロードメカニズムはプラグインを考慮し、プラグインによって提供される機能はあたかもフレームワークの一部であるかのようにプロジェクトで利用できます。</p>

<p>ですので、プラグインは基本的にsymfonyプロジェクトのために拡張機能をパッケージにしたものです。プラグインによってアプリケーションを越えて独自コードを再利用できるだけでなく、別の投稿者によって開発されたものも再利用可能でsymfonyコアにサードパーティの拡張機能を追加できます。</p>

<a name="finding.symfony.plugins" id="finding.symfony.plugins"></a><h3>symfonyのプラグインを見つける</h3>

<p>symfonyの公式サイトにはプラグイン専用のページが存在しており、つぎのURLからアクセスできます:</p>

<pre><code>http://www.symfony-project.org/plugins/
</code></pre>

<p>そこにあるそれぞれのプラグインのリストは詳細なインストールの手引きとドキュメントが一緒になった独自のページを持ちます。</p>

<p>これらのプラグインのなかにはコミュニティから投稿されたもの、symfonyのコア開発者からもたらされたものがあります。後者には、つぎのようなものが見つかります:</p>

<ul>
<li><code>sfFeedPlugin</code>: RSSとAtomフィードの操作を自動化する</li>
<li><code>sfThumbnailPlugin</code>: たとえばアップロードされたイメージのためにサムネイルを作る。</li>
<li><code>sfMediaLibraryPlugin</code>: メディアのアップロードと管理を可能にします。リッチテキスト内部でイメージの編集を可能にするリッチテキストエディタのための拡張機能を含む</li>
<li><code>sfShoppingCartPlugin</code>: ショッピングカートの運用を可能にする</li>
<li><code>sfPagerNavigationPlugin</code>: <code>sfPager</code>オブジェクトに基づいた古典的でAjaxによるページャーコントロールを提供する</li>
<li><code>sfGuardPlugin</code>: 認証、承認とsymfonyの標準的なセキュリティ機能を上回る別のユーザーの管理機能を提供する</li>
<li><code>sfPrototypePlugin</code>: prototypeとscript.aculo.usのJavaScriptファイルをスタンドアロンのライブラリとして提供する</li>
<li><code>sfSuperCachePlugin</code>: できるかぎりWebサーバーが速くなるように、webディレクトリのrootの元の<code>cache</code>ディレクトリでページを書く</li>
<li><code>sfOptimizerPlugin</code>: 運用環境において実行が速くなるようにアプリケーションのコードを最適化する(詳細はつぎの章を参照)</li>
<li><code>sfErrorLoggerPlugin</code>: データベースにすべての404エラーと500エラーをログに記録しこれらのエラーを閲覧するためのadministrationモジュールを提供する</li>
<li><code>sfSslRequirementPlugin</code>: アクションのためのSSL暗号化サポートを提供する</li>
</ul>

<p>公式サイトはビヘイビアー(behavior)と呼ばれる、Propelオブジェクトを拡張するために設計されたプラグインも提示します。これらのなかで、つぎのものが見つかります:</p>

<ul>
<li><code>sfPropelParanoidBehaviorPlugin</code>: オブジェクトの削除を無効にして、<code>deleted_at</code>カラムの更新で置き換える</li>
<li><code>sfPropelOptimisticLockBehaviorPlugin</code>: Propelオブジェクト用にオプティミスティックロック(楽観的ロック)を実装する</li>
</ul>

<p>公式サイトの専用ページを定期的に確認すべきです。いつも新しいプラグインが追加され、これらはWebアプリケーションのプログラミングの多くの面にとても便利なショートカットをもたらしてくれます。</p>

<p>公式サイトの専用ページは別にして、プラグインを配布するほかの方法はダウンロードのためのプラグインアーカイブを提供することと、PEARチャンネルでプラグインをホストすること、もしくは公開のバージョンコントロールリポジトリに保存することです。</p>

<a name="installing.a.plugin" id="installing.a.plugin"></a><h3>プラグインをインストールする</h3>

<p>プラグインのインストール作業はプラグインパッケージの作成方法によって異なります。つねに<code>README</code>ファイルかつ・もしくはプラグインのダウンロードのページ上のインストールの手引きを参照してください。また、プラグインのインストールした後につねにsymfonyのキャッシュをクリアしてください。</p>

<p>プラグインはプロジェクト単位でインストールされます。つぎのセクションで説明されるすべての方法ではすべてのプラグインのファイルを<code>myproject/plugins/pluginName/</code>ディレクトリに設置します。</p>

<a name="pear.plugins" id="pear.plugins"></a><h3>PEARプラグイン</h3>

<p>公式サイトの専用ページの一覧に記載されているプラグインはPEARパッケージとして搭載されています。これらのプラグインをインストールするには、リスト17-15で示されるように、<code>plugin-install</code>タスクに完全なURLを渡します。</p>

<p>リスト17-15 - 公式サイトの専用ページからプラグインをインストールする</p>

<pre><code>&gt; cd myproject
&gt; php symfony plugin-install http://plugins.symfony-project.com/pluginName
&gt; php symfony cc
</code></pre>

<p>代わりの方法として、プラグインをダウンロードしてディスクからインストールすることもできます。この場合、リスト17-16で示されるように、チャンネル名をパッケージアーカイブへの絶対パスに置き換えてください。</p>

<p>リスト17-16 - ダウンロードしたPEARパッケージからプラグインをインストールする</p>

<pre><code>&gt; cd myproject
&gt; php symfony plugin-install /home/path/to/downloads/pluginName.tgz
&gt; php symfony cc
</code></pre>

<p>プラグインのなかにはPEARチャンネルでホストされているものがあります。リスト17-17で示されるように、<code>plugin-install</code>タスクでこれらをインストールします。チャンネル名を入力しておくことも忘れないでください。</p>

<p>リスト17-17 - プラグインをPEARチャンネルからインストールする</p>

<pre><code>&gt; cd myproject
&gt; php symfony plugin-install channelName/pluginName
&gt; php symfony cc
</code></pre>

<p>これら3つのインストール方法はすべてPEARパッケージを利用するので、"PEARプラグイン"という用語は公式サイトのwiki、PEARチャンネル、もしくはダウンロードしたPEARパッケージからインストールされたプラグインを説明するために区別なく使われます。</p>

<a name="archive.plugins" id="archive.plugins"></a><h4>アーカイブのプラグイン</h4>

<p>プラグインのなかには単純にファイルのアーカイブとしてやってくるものがあります。それらをインストールするには、アーカイブをプロジェクトの<code>plugins/</code>ディレクトリに解凍します。プラグインが<code>web/</code>サブディレクトリを含む場合、リスト17-18で示されるように、このディレクトリをコピーするかプロジェクトの<code>web/</code>ディレクトリにシンボリックリンクを作ることを忘れないでください。最後に、キャッシュをクリアすることを忘れないでください。</p>

<p>リスト17-18 - プラグインをアーカイブからインストールする</p>

<pre><code>&gt; cd plugins
&gt; tar -zxpf myPlugin.tgz
&gt; cd ..
&gt; ln -sf plugins/myPlugin/web web/myPlugin
&gt; php symfony cc
</code></pre>

<a name="installing.plugins.from.a.version.control.repository" id="installing.plugins.from.a.version.control.repository"></a><h4>バージョン管理システムのリポジトリからプラグインをインストールする</h4>

<p>プラグインはときにバージョン管理システム用の独自のソースコードリポジトリを持つことがあります。<code>plugins/</code>ディレクトリのなかでチェックアウトするだけでこれらのプラグインをインストールできますが、プロジェクト自身がバージョン管理システムの管理下にある場合、この作業によって問題が引き起こされる可能性があります。</p>

<p>代わりの方法として、プラグインを外部依存のライブラリとして宣言することが可能で、すべてのプロジェクトのソースコードを更新するとプラグインのソースコードも更新されます。たとえば、Subversionは<code>svn:externals</code>プロパティで外部依存を保存します。ですので、リスト17-19で示されているように、このプロパティを編集してソースコードをあとで更新することでプラグインを追加できます。</p>

<p>リスト17-19 - ソースのバージョン管理リポジトリからプラグインをインストールする</p>

<pre><code>&gt; cd myproject
&gt; svn propedit svn:externals plugins
  pluginName   http://svn.example.com/pluginName/trunk
&gt; svn up
&gt; php symfony cc
</code></pre>

<blockquote class="note"><p>
  プラグインが<code>web/</code>ディレクトリを含む場合、アーカイブのプラグインに関しては同じようにそのディレクトリへのシンボリックリンクを作らなければなりません。</p>
</blockquote>

<a name="activating.a.plugin.module" id="activating.a.plugin.module"></a><h4>プラグインモジュールを有効にする</h4>

<p>プラグインのなかにはモジュール全体を含むものがあります。プラグインモジュールと古典的なモジュールの違いはプラグインモジュールが<code>myproject/myapp/modules/</code>ディレクトリに現れないことだけです(簡単にアップグレードできる状態を保つため)。リスト17-20で示されるように、<code>settings.yml</code>ファイルのなかでこれらを有効にしなければなりません。</p>

<p>リスト17-20 - プラグインモジュールを有効にする(<code>myapp/config/settings.yml</code>)</p>

<pre><code>all:
  .settings:
    enabled_modules:  [default, sfMyPluginModule]
</code></pre>

<p>これはプラグインモジュールを必要としないアプリケーションが誤ってそのプラグインを利用できるように設定する状況を避けるためです。その状況ではセキュリティの欠陥を公開してしまう可能性があります。<code>frontend</code>モジュールと<code>backend</code>モジュールを提供するプラグインを考えてください。<code>frontend</code>モジュールは<code>frontend</code>アプリケーション専用として、<code>backend</code>モジュールは<code>backend</code>アプリケーション専用として有効にする必要があります。プラグインモジュールがデフォルトで有効にされない理由はそういうわけです。</p>

<blockquote class="tip"><p>
  defaultモジュールはデフォルトで唯一有効なモジュールです。これは本当のプラグインモジュールではありません。フレームワークの<code>$sf_symfony/data_dir/modules/default/</code>に所属するからです。これは初期ページと、404エラー用のデフォルトページとクレデンシャルが必要なエラーページを提供するモジュールです。symfonyのデフォルトページを使いたくない場合、このモジュールを<code>enabled_modules</code>設定から除外します。</p>
</blockquote>

<a name="listing.the.installed.plugins" id="listing.the.installed.plugins"></a><h4>インストールしたプラグインの一覧を表示する</h4>

<p>プロジェクトの<code>plugins/</code>ディレクトリをざっと見るとプラグインがインストールされている場所がわかります。そして<code>plugin-list</code>タスクはより詳細な情報を示します: バージョン番号とインストールしたそれぞれのプラグインのチャンネル名です。</p>

<p>リスト17-21 - インストールされたプラグインの一覧</p>

<pre><code>&gt; cd myproject
&gt; php symfony plugin-list

Installed plugins:
sfPrototypePlugin               1.0.0-stable # pear.symfony-project.com (symfony)
sfSuperCachePlugin              1.0.0-stable # pear.symfony-project.com (symfony)
sfThumbnail                     1.1.0-stable # pear.symfony-project.com (symfony)
</code></pre>

<a name="upgrading.and.uninstalling.plugins" id="upgrading.and.uninstalling.plugins"></a><h4>プラグインのアップグレードとアンインストール</h4>

<p>PEARのプラグインをアンインストールするには、リスト17-22で示されるように、プロジェクトのrootディレクトリから<code>plugin-uninstall</code>タスクを呼び出します。プラグインの名前にインストールしたチャンネルをプレフィックスとして追加しなければなりません(このチャンネルを決めるために<code>plugin-list</code>タスクを使います)。</p>

<p>リスト17-22 - プラグインをアンインストールする</p>

<pre><code>&gt; cd myproject
&gt; php symfony plugin-uninstall pear.symfony-project.com/sfPrototypePlugin
&gt; php symfony cc
</code></pre>

<blockquote class="tip"><p>
  チャンネルのなかにはエイリアスを持つものがあります。たとえば、<code>pear.symfony-project.com</code>チャンネルは<code>symfony</code>とも見なされます。このことはリスト17-22のように<code>php symfony plugin-uninstall symfony/sfPrototypePlugin</code>を呼び出すだけで<code>sfPrototypePlugin</code>をアンインストールできることを意味します。</p>
</blockquote>

<p>アーカイブからインストールしたプラグインもしくはSVNリポジトリからインストールしたプラグインをアンインストールするには、プロジェクトの<code>plugins/</code>と<code>web/</code>ディレクトリからプラグインファイルを手動で削除してキャッシュをクリアします。</p>

<p>プラグインをアップグレードするには、<code>plugin-upgrade</code>タスク(PEARプラグインの場合)もしくは<code>svn update</code>を実行します(バージョン管理システムのリポジトリからプラグインを入手した場合)。アーカイブからインストールしたプラグインは簡単にアップグレードできません。</p>

<a name="anatomy.of.a.plugin" id="anatomy.of.a.plugin"></a><h3>プラグインの分析</h3>

<p>プラグインはPHPで書かれています。アプリケーションの編成方法を理解しているのであれば、プラグインの構造を理解できます。</p>

<a name="plugin.file.structure" id="plugin.file.structure"></a><h4>プラグインのファイル構造</h4>

<p>プラグインのディレクトリはおおよそプロジェクトのディレクトリと同じように編成されています。必要な時にsymfonyによって自動的にロードされるようにするためにプラグインファイルは正しいディレクトリに存在しなければなりません。ファイル構造の記述に関してはリスト17-23をご覧ください。</p>

<p>リスト17-23 - プラグインのファイル構造</p>

<pre><code>pluginName/
  config/
    *schema.yml        // データスキーマ
    *schema.xml
    config.php         // 特定のプラグインのコンフィギュレーション
  data/
    generator/
      sfPropelAdmin
        */             // administrationジェネレーターテーマ
          template/
          skeleton/
    fixtures/
      *.yml            // フィクスチャファイル
    tasks/
      *.php            // Pakeタスク
  lib/
    *.php              // クラス
    helper/
      *.php            // ヘルパー
    model/
      *.php            // モデルクラス
  modules/
    */                 // モジュール
      actions/
        actions.class.php
      config/
        module.yml
        view.yml
        security.yml
      templates/
        *.php
      validate/
        *.yml
  web/
    *                  // アセット
</code></pre>

<a name="plugin.abilities" id="plugin.abilities"></a><h4>プラグインの機能</h4>

<p>プラグインは多くのものを含みます。コマンドラインでタスクを呼び出すときに実行中のアプリケーションはこれらの内容を自動的に考慮します。しかしプラグインを適切に機能させるには、いくつかの規約を遵守しなければなりません:</p>

<ul>
<li>データベースのスキーマは<code>propel-</code>タスクによって検出されます。<code>propel-build-model</code>タスクを呼び出すと、プロジェクトモデルとすべてのプラグインモデルがリビルドされます。リスト17-24で示されるように、プラグインスキーマはつねに<code>plugins.pluginName.lib.model</code>形式で<code>package</code>属性を持つことに注意してください。</li>
</ul>

<p>リスト17-24 - スキーマ宣言の例(<code>myPlugin/config/schema.yml</code>)</p>

<pre><code>propel:
  _attributes:    { package: plugins.myPlugin.lib.model }
  my_plugin_foobar:
    _attributes:    { phpName: myPluginFoobar }
      id:
      name:           { type: varchar, size: 255, index: unique }
      ...
</code></pre>

<ul>
<li>プラグインの設定はプラグインの起動スクリプト(<code>config.php</code>)に含まれています。このファイルはアプリケーションとプロジェクト設定のあとで実行されるので、symfonyはその時点ですでに起動しています。たとえば、ディレクトリをPHPのincludeパスに追加するため、もしくはミックスインで既存のクラスを拡張するためなどに、このファイルを利用できます。</li>
<li>プラグインの<code>data/fixtures/</code>ディレクトリに設置されたフィクスチャファイルは<code>propel-load-data</code>タスクで処理されます。</li>
<li>プラグインがインストールされると同時にタスクはsymfonyのコマンドラインですぐに利用できるようになります。タスクにたとえばプレフィックスとしてプラグインの名前などの意味のある名前をつけることはよい習慣です。プラグインによって追加されたタスクを含む利用可能なタスクの一覧を見るには、<code>symfony</code>コマンドを実行してください。</li>
<li>プロジェクトの<code>lib/</code>フォルダーに設置されたクラスのようにカスタムクラスはオートロードされます。</li>
<li>テンプレートのなかで<code>use_helper()</code>ヘルパーを呼び出すときにヘルパーは自動的に発見されます。これらはプラグインの<code>lib/</code>ディレクトリの1つの<code>helper/</code>サブディレクトリに存在しなければなりません。</li>
<li><code>myplugin/lib/model/</code>ディレクトリ内のモデルクラスは(<code>myplugin/lib/model/om/</code>ディレクトリと<code>myplugin/lib/model/map/</code>ディレクトリ)内部のPropelビルダによって生成されたモデルクラスを専門に扱います。もちろんこれらもオートロードされます。独自プロジェクトのディレクトリ内で生成されたプラグインのモデルクラスはオーバーライドできません。</li>
<li>アプリケーションの<code>enabled_modules</code>設定で宣言すれば、モジュールは外部からアクセス可能な新しいアクションを提供します。</li>
<li>サーバーはWebアセット(イメージ、スクリプト、スタイルシートなど)を利用できます。コマンドライン経由でプラグインをインストールしたとき、symfonyはシステムが許可するのであればプロジェクトの<code>web/</code>ディレクトリにシンボリックリンクを作るもしくは<code>web/</code>ディレクトリの内容をプロジェクトをディレクトリにコピーします。プラグインがアーカイブもしくはバージョン管理ツールのリポジトリからインストールされた場合、手動で<code>web/</code>ディレクトリにコピーしなければなりません(プラグインに添付されている<code>README</code>に記載されています)。</li>
</ul>

<a name="manual.plugin.setup" id="manual.plugin.setup"></a><h4>手動によるプラグインのセットアップ</h4>

<p><code>plugin-install</code>タスクが独自に処理できない要素がいくつかあります。インストール作業の間にこれらを手動でセットアップする必要があります:</p>

<ul>
<li>カスタムアプリケーション設定はプラグインのコードで使われますが(たとえば、<code>sfConfig::get('app_myplugin_foo')</code>を利用する)、デフォルト値をプラグインの<code>config/</code>ディレクトリに設置された<code>app.yml</code>ファイルに設定できません。デフォルト値を処理するには、<code>sfConfig::get()</code>メソッドの2番目の引数を使います。設定はまだアプリケーションレベルでオーバーライドできます(リスト17-25で例をご覧ください)。</li>
<li>カスタムルーティングルールはアプリケーションの<code>routing.yml</code>に手動で追加しなければなりません。</li>
<li>カスタムフィルターはアプリケーションの<code>filters.yml</code>に手動で追加しなければなりません。</li>
<li>カスタムファクトリはアプリケーションの<code>factories.yml</code>に手動で追加しなければなりません。</li>
</ul>

<p>一般的に言えば、アプリケーションの設定ファイルの1つになるすべての設定は手動で追加しなければなりません。このような手動のセットアップが必要なプラグインは<code>README</code>ファイルで詳細なインストール方法を説明しています。</p>

<a name="customizing.a.plugin.for.an.application" id="customizing.a.plugin.for.an.application"></a><h4>アプリケーションのためにプラグインをカスタマイズする</h4>

<p>プラグインをカスタマイズしたいときは、決して<code>plugins/</code>ディレクトリ内で見つかるコードを変更してはなりません。これを行うと、プラグインをアップグレードするときにすべての修正内容が失われてしまいます。必要なカスタマイズを行うために、プラグインはカスタム設定を提供し、オーバーライドをサポートします。</p>

<p>リスト17-25で示されるように、よく設計されたプラグインはアプリケーションの<code>app.yml</code>ファイルで変更できる設定を利用します。</p>

<p>リスト17-25 - アプリケーションの設定を利用するプラグインをカスタマイズする</p>

<pre class="php"><span class="co1">// プラグインのコードの例</span>
<span class="re0">$foo</span> <span class="sy0">=</span> sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'app_my_plugin_foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// アプリケーションのapp.ymlで'foo'のデフォルト値('bar')を変更する</span>
all<span class="sy0">:</span>
  my_plugin<span class="sy0">:</span>
    foo<span class="sy0">:</span>       barbar</pre>

<p>モジュールの設定とデフォルト値はプラグインの<code>README</code>ファイルで詳しく説明されています。</p>

<p>独自のアプリケーション内部で同じ名前のモジュールを作成することでプラグインモジュールのデフォルトの内容を置き換えることができます。プラグイン要素の代わりにアプリケーション要素が使われているので、本当のオーバーライドではありません。プラグインの名前と同じ名前のテンプレートと設定ファイルを作ればプラグインモジュールは立派に機能します。</p>

<p>一方で、アクションをオーバーライドする機能を持つモジュールをプラグインに持たせたい場合、プラグインモジュール内の<code>actions.class.php</code>のメソッドがアプリケーションモジュールの<code>actions.class.php</code>によって継承できるように、<code>actions.class.php</code>は空でなければならずオートロードクラスから継承しなければなりません。お手本に関してはリスト17-26を参照してください。</p>

<p>リスト17-26 - プラグインのアクションをカスタマイズする</p>

<pre class="php"><span class="co1">// myPlugin/modules/mymodule/lib/myPluginmymoduleActions.class.phpのなか</span>
<span class="kw2">class</span> myPluginmymoduleActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ここに何らかのコード</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// myPlugin/modules/mymodule/actions/actions.class.phpにて</span>
&nbsp;
<span class="kw1">require_once</span> <span class="kw3">dirname</span><span class="br0">&#40;</span><span class="kw4">__FILE__</span><span class="br0">&#41;</span><span class="sy0">.</span><span class="st_h">'/../lib/myPluginmymoduleActions.class.php'</span><span class="sy0">;</span>
&nbsp;
<span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> myPluginmymoduleActions
<span class="br0">&#123;</span>
  <span class="co1">// なし</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="co1">// myapp/modules/mymodule/actions/actions.class.phpにて</span>
<span class="kw2">class</span> mymoduleActions <span class="kw2">extends</span> myPluginmymoduleActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeIndex<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="co1">// ここでプラグインのコードをオーバーライドする</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<a name="how.to.write.a.plugin" id="how.to.write.a.plugin"></a><h3>プラグインの書き方</h3>

<p><code>plugin-install</code>タスクではPEARパッケージ形式のプラグインのみがインストールされます。このようなプラグインは公式サイトの専用ページ、PEARチャンネル経由もしくはダウンロードできる通常のファイルとして配布されていることを覚えておいてください。プラグインを編集したい場合は、単純なアーカイブよりもPEARパッケージとして公開したほうがベターでしょう。加えて、プラグインをPEARパッケージにすればアップグレード作業が簡単になり、依存関係の宣言が可能で、自動的にアセットを<code>web/</code>ディレクトリにデプロイできます。</p>

<a name="file.organization" id="file.organization"></a><h4>ファイルのコンフィギュレーション</h4>

<p>新しい機能を開発し、プラグインとしてパッケージにすることを考えてみましょう。最初の段階はファイルを論理的に編成して、symfonyのロードメカニズムが必要なときにこれらのファイルを見つけることができるようにしましょう。この目的のために、リスト17-23で示されているディレクトリ構造に従う必要があります。リスト17-27は<code>sfSamplePlugin</code>プラグインのためのファイル構造の例を示しています。</p>

<p>リスト17-27 - プラグインとしてパッケージにするファイルの一覧の例</p>

<pre><code>sfSamplePlugin/
  README
  LICENSE
  config/
    schema.yml
  data/
    fixtures/
      fixtures.yml
    tasks/
      sfSampleTask.php
  lib/
    model/
      sfSampleFooBar.php
      sfSampleFooBarPeer.php
    validator/
      sfSampleValidator.class.php
  modules/
    sfSampleModule/
      actions/
        actions.class.php
      config/
        security.yml
      lib/
        BasesfSampleModuleActions.class.php
      templates/
        indexSuccess.php
  web/
    css/
      sfSampleStyle.css
    images/
      sfSampleImage.png
</code></pre>

<p>編集に関して、プラグインのディレクトリの位置(リスト17-27の<code>sfSamplePlugin/</code>)は重要ではありません。これはディスク上の任意の場所に設置できます。</p>

<blockquote class="tip"><p>
  既存のプラグインを練習問題として考え、初めてプラグインを作るさいには、これらの名前の規約とファイルの構造を再現してみてください。</p>
</blockquote>

<a name="creating.the.package.xml.file" id="creating.the.package.xml.file"></a><h4>package.xmlファイルを作る</h4>

<p>プラグイン編集のつぎの段階はプラグインディレクトリのrootで<code>package.xml</code>ファイルを追加することです。<code>package.xml</code>はPEARの構文に従います。リスト17-28の典型的なsymfonyプラグインの<code>package.xml</code>をご覧ください。</p>

<p>リスト17-28 - symfonyプラグイン用の<code>package.xml</code></p>

<pre class="xml"><span class="sc3"><span class="re1">&lt;?xml</span> <span class="re0">version</span>=<span class="st0">&quot;1.0&quot;</span> <span class="re0">encoding</span>=<span class="st0">&quot;UTF-8&quot;</span><span class="re2">?&gt;</span></span>
<span class="sc3"><span class="re1">&lt;package</span> <span class="re0">packagerversion</span>=<span class="st0">&quot;1.4.6&quot;</span> <span class="re0">version</span>=<span class="st0">&quot;2.0&quot;</span> <span class="re0">xmlns</span>=<span class="st0">&quot;http://pear.php.net/dtd/package-2.0&quot;</span> <span class="re0">xmlns:tasks</span>=<span class="st0">&quot;http://pear.php.net/dtd/tasks-1.0&quot;</span> <span class="re0">xmlns:xsi</span>=<span class="st0">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="re0">xsi:schemaLocation</span>=<span class="st0">&quot;http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd&quot;</span><span class="re2">&gt;</span></span>
 <span class="sc3"><span class="re1">&lt;name<span class="re2">&gt;</span></span></span>sfSamplePlugin<span class="sc3"><span class="re1">&lt;/name<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;channel<span class="re2">&gt;</span></span></span>pear.symfony-project.com<span class="sc3"><span class="re1">&lt;/channel<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;summary<span class="re2">&gt;</span></span></span>symfony sample plugin<span class="sc3"><span class="re1">&lt;/summary<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;description<span class="re2">&gt;</span></span></span>Just a sample plugin to illustrate PEAR packaging<span class="sc3"><span class="re1">&lt;/description<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;lead<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;name<span class="re2">&gt;</span></span></span>Fabien POTENCIER<span class="sc3"><span class="re1">&lt;/name<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;user<span class="re2">&gt;</span></span></span>fabpot<span class="sc3"><span class="re1">&lt;/user<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;email<span class="re2">&gt;</span></span></span>fabien.potencier@symfony-project.com<span class="sc3"><span class="re1">&lt;/email<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;active<span class="re2">&gt;</span></span></span>yes<span class="sc3"><span class="re1">&lt;/active<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;/lead<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;date<span class="re2">&gt;</span></span></span>2006-01-18<span class="sc3"><span class="re1">&lt;/date<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;time<span class="re2">&gt;</span></span></span>15:54:35<span class="sc3"><span class="re1">&lt;/time<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;version<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;release<span class="re2">&gt;</span></span></span>1.0.0<span class="sc3"><span class="re1">&lt;/release<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;api<span class="re2">&gt;</span></span></span>1.0.0<span class="sc3"><span class="re1">&lt;/api<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;/version<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;stability<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;release<span class="re2">&gt;</span></span></span>stable<span class="sc3"><span class="re1">&lt;/release<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;api<span class="re2">&gt;</span></span></span>stable<span class="sc3"><span class="re1">&lt;/api<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;/stability<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;license</span> <span class="re0">uri</span>=<span class="st0">&quot;http://www.symfony-project.org/license&quot;</span><span class="re2">&gt;</span></span>MIT license<span class="sc3"><span class="re1">&lt;/license<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;notes<span class="re2">&gt;</span></span></span>-<span class="sc3"><span class="re1">&lt;/notes<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;contents<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;/&quot;</span><span class="re2">&gt;</span></span>
   <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;README&quot;</span> <span class="re2">/&gt;</span></span>
   <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;LICENSE&quot;</span> <span class="re2">/&gt;</span></span>
   <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;config&quot;</span><span class="re2">&gt;</span></span>
    <span class="sc-1">&lt;!-- model --&gt;</span>
    <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;schema.yml&quot;</span> <span class="re2">/&gt;</span></span>
   <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;data&quot;</span><span class="re2">&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;fixtures&quot;</span><span class="re2">&gt;</span></span>
     <span class="sc-1">&lt;!-- fixtures --&gt;</span>
     <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;fixtures.yml&quot;</span> <span class="re2">/&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;tasks&quot;</span><span class="re2">&gt;</span></span>
     <span class="sc-1">&lt;!-- tasks --&gt;</span>
     <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;sfSampleTask.php&quot;</span> <span class="re2">/&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;lib&quot;</span><span class="re2">&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;model&quot;</span><span class="re2">&gt;</span></span>
     <span class="sc-1">&lt;!-- model classes --&gt;</span>
     <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;sfSampleFooBar.php&quot;</span> <span class="re2">/&gt;</span></span>
     <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;sfSampleFooBarPeer.php&quot;</span> <span class="re2">/&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;validator&quot;</span><span class="re2">&gt;</span></span>
     <span class="sc-1">&lt;!-- validators -&gt;&gt;</span>
<span class="sc-1">     &lt;file role=&quot;data&quot; name=&quot;sfSampleValidator.class.php&quot; /&gt;</span>
<span class="sc-1">    &lt;/dir&gt;</span>
<span class="sc-1">   &lt;/dir&gt;</span>
<span class="sc-1">   &lt;dir name=&quot;modules&quot;&gt;</span>
<span class="sc-1">    &lt;dir name=&quot;sfSampleModule&quot;&gt;</span>
<span class="sc-1">     &lt;file role=&quot;data&quot; name=&quot;actions/actions.class.php&quot; /&gt;</span>
<span class="sc-1">     &lt;file role=&quot;data&quot; name=&quot;config/security.yml&quot; /&gt;</span>
<span class="sc-1">     &lt;file role=&quot;data&quot; name=&quot;lib/BasesfSampleModuleActions.class.php&quot; /&gt;</span>
<span class="sc-1">     &lt;file role=&quot;data&quot; name=&quot;templates/indexSuccess.php&quot; /&gt;</span>
<span class="sc-1">    &lt;/dir&gt;</span>
<span class="sc-1">   &lt;/dir&gt;</span>
<span class="sc-1">   &lt;dir name=&quot;web&quot;&gt;</span>
<span class="sc-1">    &lt;dir name=&quot;css&quot;&gt;</span>
<span class="sc-1">     &lt;!-- stylesheets --&gt;</span>
     <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;sfSampleStyle.css&quot;</span> <span class="re2">/&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;dir</span> <span class="re0">name</span>=<span class="st0">&quot;images&quot;</span><span class="re2">&gt;</span></span>
     <span class="sc-1">&lt;!-- images --&gt;</span>
     <span class="sc3"><span class="re1">&lt;file</span> <span class="re0">role</span>=<span class="st0">&quot;data&quot;</span> <span class="re0">name</span>=<span class="st0">&quot;sfSampleImage.png&quot;</span> <span class="re2">/&gt;</span></span>
    <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;/dir<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;/contents<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;dependencies<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;required<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;php<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;min<span class="re2">&gt;</span></span></span>5.0.0<span class="sc3"><span class="re1">&lt;/min<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;/php<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;pearinstaller<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;min<span class="re2">&gt;</span></span></span>1.4.1<span class="sc3"><span class="re1">&lt;/min<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;/pearinstaller<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;package<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;name<span class="re2">&gt;</span></span></span>symfony<span class="sc3"><span class="re1">&lt;/name<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;channel<span class="re2">&gt;</span></span></span>pear.symfony-project.com<span class="sc3"><span class="re1">&lt;/channel<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;min<span class="re2">&gt;</span></span></span>1.0.0<span class="sc3"><span class="re1">&lt;/min<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;max<span class="re2">&gt;</span></span></span>1.1.0<span class="sc3"><span class="re1">&lt;/max<span class="re2">&gt;</span></span></span>
    <span class="sc3"><span class="re1">&lt;exclude<span class="re2">&gt;</span></span></span>1.1.0<span class="sc3"><span class="re1">&lt;/exclude<span class="re2">&gt;</span></span></span>
   <span class="sc3"><span class="re1">&lt;/package<span class="re2">&gt;</span></span></span>
  <span class="sc3"><span class="re1">&lt;/required<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;/dependencies<span class="re2">&gt;</span></span></span>
 <span class="sc3"><span class="re1">&lt;phprelease</span> <span class="re2">/&gt;</span></span>
 <span class="sc3"><span class="re1">&lt;changelog</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;/package<span class="re2">&gt;</span></span></span></pre>

<p>ここで注目すべき部分は<code>&lt;contents&gt;</code>タグと<code>&lt;dependencies&gt;</code>タグで、つぎに説明します。残りのタグに関しては、symfony固有のものではありませんので、<code>package.xml</code>フォーマットに関する詳細な内容はPEARオンラインマニュアル(<a href="http://pear.php.net/manual/">http://pear.php.net/manual/</a>) を参照してください。</p>

<a name="contents" id="contents"></a><h4>内容</h4>

<p><code>&lt;contents&gt;</code>タグはプラグインのファイル構造を記述しなければならない場所です。このタグはコピーするファイルとその場所をPEARに伝えます。<code>&lt;dir&gt;</code>タグと<code>&lt;file&gt;</code>タグでファイル構造を記述してください。すべての<code>&lt;file&gt;</code>タグは<code>role="data"</code>属性を持たなければなりません。リスト17-28の<code>&lt;contents&gt;</code>タグの部分はリスト17-27の正しいディレクトリ構造を記載しています。</p>

<blockquote class="note"><p>
  <code>&lt;dir&gt;</code>タグの使用は義務ではありません。<code>&lt;file&gt;</code>タグ内で相対パスを<code>name</code>の値として利用できるからです。<code>package.xml</code>ファイルを読みやすくするためにお勧めです。</p>
</blockquote>

<a name="plugin.dependencies" id="plugin.dependencies"></a><h4>プラグインの依存関係</h4>

<p>任意のバージョンのPHP、PEAR、symfony、PEARパッケージ、もしくはほかのプラグインの一式で動くようにプラグインは設計されています。<code>&lt;dependencies&gt;</code>タグでこれらの依存関係を宣言すれば必要なパッケージがすでにインストールされていることを確認してそうでなければ例外を起動するようPEARに伝えることになります。</p>

<p>最小要件として、少なくとも開発環境に対応したPHP、PEARとsymfonyへの依存関係をつねに宣言します。何を追加すればよいのかわからなければ、PHP 5.0、PEAR 1.4とsymfony 1.0の要件を追加してください。</p>

<p>それぞれのプラグインに対してsymfonyの最大のバージョン番号を追加することも推奨されます。これによって上位バージョンのsymfonyでプラグインを使うときにエラーメッセージが表示され、プラグインを再リリースするまえにこのバージョンでプラグインが正しく動作するのかを確認することをプラグインの作者に義務づけます。無言でプラグインの動作が失敗するよりも警告を発してダウンロードとアップグレードするほうがベターです。</p>

<a name="building.the.plugin" id="building.the.plugin"></a><h4>プラグインをビルドする</h4>

<p>PEARコンポーネントはパッケージの<code>.tgz</code>アーカイブを作るコマンド(<code>pear package</code>)を持ちます。リスト17-29では、<code>package.xml</code>を含むディレクトリでこのコマンドを呼び出しています。</p>

<p>リスト17-29 - プラグインをPEARパッケージにする</p>

<pre><code>&gt; cd sfSamplePlugin
&gt; pear package

Package sfSamplePlugin-1.0.0.tgz done
</code></pre>

<p>いったんプラグインのパッケージがビルドされたら、リスト17-30で示されるように、あなたの環境にこれをインストールして動作を確認してください。</p>

<p>リスト17-30 - プラグインをインストールする</p>

<pre><code>&gt; cp sfSamplePlugin-1.0.0.tgz /home/production/myproject/
&gt; cd /home/production/myproject/
&gt; php symfony plugin-install sfSamplePlugin-1.0.0.tgz
</code></pre>

<p><code>&lt;contents&gt;</code>タグにある説明にしたがって、パッケージにされたファイルは最終的にプロジェクトの異なるディレクトリに設置されます。リスト17-31はインストールのあとで<code>sfSamplePlugin</code>のファイルが設置される場所を示しています。</p>

<p>リスト17-31 - プラグインファイルは<code>plugin/</code>ディレクトリと<code>web/</code>ディレクトリにインストールされる</p>

<pre><code>plugins/
  sfSamplePlugin/
    README
    LICENSE
    config/
      schema.yml
    data/
      fixtures/
        fixtures.yml
      tasks/
        sfSampleTask.php
    lib/
      model/
        sfSampleFooBar.php
        sfSampleFooBarPeer.php
      validator/
        sfSampleValidator.class.php
    modules/
      sfSampleModule/
        actions/
          actions.class.php
        config/
          security.yml
        lib/
          BasesfSampleModuleActions.class.php
        templates/
          indexSuccess.php
web/
  sfSamplePlugin/               ## システム次第で、コピーもしくはシンボリックリンク
    css/
      sfSampleStyle.css
    images/
      sfSampleImage.png
</code></pre>

<p>このプラグインのふるまいをアプリケーションでテストしてください。きちんと動くのであれば、プラグインを複数のプロジェクトにまたがって配布するもしくはsymfonyコミュニティに寄付する準備ができています。</p>

<a name="hosting.your.plugin.in.the.symfony.project.website" id="hosting.your.plugin.in.the.symfony.project.website"></a><h4>公式サイトでプラグインを配布する</h4>

<p>symfonyのプラグインは以下の手順にしたがって<code>symfony-project.org</code>のWebサイトで配布されるときにもっとも幅広い利用者を得ます。独自プラグインをつぎのような方法で配布できます:</p>

<ol>
<li><code>README</code>ファイルにプラグインのインストール方法と使いかたが、<code>LICENSE</code>ファイルにはライセンスの詳細が記述されていることを確認する。<code>README</code>はMarkdownの構文 (<a href="http://daringfireball.net/projects/markdown/syntax">http://daringfireball.net/projects/markdown/syntax</a>) で記述する。</li>
<li>公式サイトのアカウント (http://www.symfony-project.org/user/new) を作りプラグインのページ (http://www.symfony-project.org/plugins/new) を作る。</li>
<li><code>pear package</code>コマンドを呼び出してプラグイン用のPEARパッケージを作りテストする。PEARパッケージの名前は<code>sfSamplePlugin-1.0.0.tgz</code> (1.0.0はプラグインのバージョン)でなければならない。</li>
<li>PEARパッケージをアップロードする (<code>sfSamplePlugin-1.0.0.tgz</code>)。</li>
<li>アップロードしたプラグインは一覧ページ (<a href="http://www.symfony-project.org/plugins/">http://www.symfony-project.org/plugins/</a>) に表示される。</li>
</ol>

<p>この手続きを行えば、ユーザーはプロジェクトのディレクトリでつぎのコマンドを入力するだけでプラグインをインストールできるようになります:</p>

<pre><code>&gt; php symfony plugin-install http://plugins.symfony-project.com/sfSamplePlugin
</code></pre>

<a name="naming.conventions" id="naming.conventions"></a><h4>命名規約</h4>

<p><code>plugin/</code>ディレクトリをきれいに保つために、すべてのプラグインの名前がcamelCaseであり<code>Plugin</code>のサフィックスで終わることを確認してください(たとえば、<code>shoppingCartPlugin</code>、<code>feedPlugin</code>)。プラグインに名前をつけるまえに、同じ名前のプラグインが存在しないことを確認してください。</p>

<blockquote class="note"><p>
  Propelに依存するプラグインの名前は<code>Propel</code>を含みます。たとえば、Propelのデータアクセスオブジェクトを利用する認証プラグインは<code>sfPropelAuth</code>という名前になります。</p>
</blockquote>

<p>プラグインには使用条件と選んだライセンスを説明する<code>LICENSE</code>ファイルをつねに含めなければなりません。バージョンの履歴、プラグインの目的、効果、インストールと設定の手引きなどを含めることも推奨されます。</p>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>symfonyのクラスはアプリケーションレベルで修正できる機能を提供する<code>sfMixer</code>フックを含みます。ミックスイン(mixin)のメカニズムはPHPの制約が禁止している実行時のクラスの多重継承とオーバーライドを可能にします。ですのでそのためにコアクラスを修正しなければならないとしても、またファクトリ(factory)の設定がそこに存在するとしてもsymfonyの機能を簡単に拡張できます。</p>

<p>すでに多くの拡張機能(エクステンション)が存在し、プラグインとしてパッケージが作成されています。symfonyのコマンドラインによってインストール、アップグレード、アンインストールするのが簡単です。プラグインをPEARパッケージを作成するのと同じぐらい簡単で、複数のアプリケーションをまたがって再利用できます。</p>

<p>symfony公式サイトのwikiには多くのプラグインが含まれ、あなた自身のプラグインも追加できます。これであなたは方法を理解したので、私たちsymfonyの開発者はあなたが多くの便利な拡張機能でsymfonyコアを強化して下さることを望んでおります！</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="16-Application-Management-Tools.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="18-Performance.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
